package testrank.predictors;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Map.Entry;

import testrank.Conflict;
import testrank.Predictor;
import testrank.TestRankConstants;

/**
 * @author Hagai Cibulski
 */
public class ExecutionCountPredictor implements Predictor {

	private static final long serialVersionUID = 1L;

	private static final int N_ITERATIONS = 1;

	/**
	 * conflict[function][test]
	 */
	protected Map<Conflict, Integer> conflicts = new HashMap<Conflict, Integer>();
	
	/**
	 * for recursive normalization
	 */
	// transient if NOT CURENTLY IN USE
	protected transient Map<Conflict, Double> scores = new HashMap<Conflict, Double>();

	// transient if NOT CURENTLY IN USE
	protected transient Map<String, Integer> totalExecutionCountDuringTest = new HashMap<String, Integer>();
	
	protected SortedSet<String> allTests = new TreeSet<String>(); 
	
	public boolean isCovering(String methodSignature, String testSignature) {
		Conflict conflict = new Conflict(methodSignature, testSignature);
		Integer count = conflicts.get(conflict);
		return count != null && count > 0;
	}
	
	public Set<String> getCoveringTests(String methodSignature) {
		Set<String> r = new HashSet<String>();
		Set<Conflict> allConflicts = conflicts.keySet();
		for (Conflict c : allConflicts) {
			if (c.getMethodSignature().equals(methodSignature)) {
				r.add(c.getTestSignature());
			}
		}
		return r;
	}
	
	public Set<String> getCoveredMethods(String testSignature) {
		Set<String> r = new HashSet<String>();
		Set<Conflict> allConflicts = conflicts.keySet();
		for (Conflict c : allConflicts) {
			if (c.getTestSignature().equals(testSignature)) {
				r.add(c.getMethodSignature());
			}
		}
		return r;
	}

	
	//@Override
	public double rankFailure(String methodSignature, String testSignature) {
		Conflict conflict = new Conflict(methodSignature, testSignature);
		Integer count = conflicts.get(conflict);

		if (count == null)
			return 0;
		
		if (TestRankConstants.USE_ALT_NORMALIZATION) {
			// normalize the conflict count by the total execution count of the test
			Integer total = totalExecutionCountDuringTest.get(testSignature);
			double rank = count / ((double)total);
			return rank;
		}
		else {
			double rank = (double)count / (count + 1.0);
			return rank;
		}
	}
	
	/**
	 * this is the recursive normalization version
	 */
	//@Override
	public double rankFailure_recnorm(String methodSignature, String testSignature) {
		Conflict conflict = new Conflict(methodSignature, testSignature);
		Double s = scores.get(conflict);

		if (s == null)
			return 0;
		
		return s;
	}


	@Override
	public void begin(String methodSignature, String testSignature) {
		allTests.add(testSignature);
			
		Conflict conflict = new Conflict(methodSignature, testSignature);
		Integer count = conflicts.get(conflict);
		if (count == null) {
			count = 0;
		}
		count++;
		conflicts.put(conflict, count);
		
		// update total execution count of methods in test
		Integer total = totalExecutionCountDuringTest.get(testSignature);
		if (total == null) {
			total = 0;
		}
		total++;
		totalExecutionCountDuringTest.put(testSignature, total);
	}

	@Override
	public void end(String methodSignature, String testSignature) {
		
	}

	@Override
	public String toString() {
		return  getName() + ":" + conflicts.toString().replace(", Conflict", ",\nConflict") + "\nTotal Conflicts = " + conflicts.size();
	}
	
	public void printConflicts() {
		System.out.println(toString());
	}

	public List<String> getAllTests() {
		return new ArrayList<String>(allTests);
	}
	
	public Set<Conflict> getAllConflicts() {
		return conflicts.keySet();
	}
	
	public double rankFailureConverge(String methodSignature, String testSignature) {
		Conflict conflict = new Conflict(methodSignature, testSignature);
		Integer count = conflicts.get(conflict);
		if (count == null)
			return 0;
		return (double)count / (count + 1.0);
	}
	
	@Override
	public String getName() {
		return "EXCNT";
	}
	
	public void processScores() {
		for (Entry<Conflict, Integer> e : conflicts.entrySet()) {
			scores.put(e.getKey(), e.getValue().doubleValue());
		}
		
		RecursiveNormalizationUtil.processScores(scores, N_ITERATIONS, true);
	}
}

